#include "MVData.h"
#include "MVDataAgent.h"

CMVData::CMVData( TYPE_MVDATA_STORE type, const std::string dataname )
{
	m_plstFrame = new std::list<CMVDataFrame*>;
	m_strDataName = dataname;
	m_bModified = false;

	m_iLastReadFrameNumber = -1;
	m_iLastWriteFrameNumber = -1;

	Open( type, dataname );
}

CMVData::CMVData()
{
	m_bReadyToUse = false;
	m_plstFrame = new std::list<CMVDataFrame*>;
	m_strDataName = "";
	m_bModified = false;

	m_iLastReadFrameNumber = -1;
	m_iLastWriteFrameNumber = -1;

}

CMVData::~CMVData()
{
	delete m_pAgent;
	delete m_plstFrame;
}


int CMVData::CreateNew( TYPE_MVDATA_STORE type, const std::string dataname, int width, int height )
{
	m_pAgent = CMVDataAgent::CreateMVDataAgent( this, type, dataname );

	if ( NULL == m_pAgent )
	{
		return -1;
	}

	m_bReadyToUse = true;
	m_strDataName = dataname;
	return 0;
}

// Open a existing file and read the content.
int CMVData::Open( TYPE_MVDATA_STORE type, const std::string dataname )
{

	int ret = CreateNew( type, dataname );

	if ( ret == 0 )
	{
		m_bReadyToUse = true;
	}

	if ( ReadAll() == 0 )
	{
		return 0;
	}

	return -1;
}


int CMVData::Clear()
{
	if ( !m_bReadyToUse )
	{
		return -1;
	}

	m_plstFrame->clear();
	return 0;
}


int CMVData::ReadAll()
{

	if( !m_bReadyToUse )
	{
		return -1;
	}

	return m_pAgent->ReadMVData();
}

int CMVData::WriteAll()
{
	if ( m_bModified  )
	{
		return m_pAgent->WriteMVData();
	}
	else
	{
		return 0;
	}
}

CMVDataFrame * CMVData::_GetFrameByNumber( int framenumber ) const
{
	if( !m_bReadyToUse )
	{
		return NULL;
	}

	if( framenumber < 0 || m_plstFrame->size() == 0 )
	{
		return NULL;
	}

	for( std::list<CMVDataFrame*>::iterator i=m_plstFrame->begin(); i!=m_plstFrame->end(); i++ )
	{
		if( (*i)->GetFrameNumber() == framenumber )
		{
			return *i;
		}
	}
	return NULL;
}

const CMVDataFrame * CMVData::GetNextFrame( const CMVDataFrame * curr_frame ) const
{
	if( !m_bReadyToUse )
	{
		return NULL;
	}

	if ( m_plstFrame->empty() )
	{
		return NULL;
	}

	if( NULL == curr_frame )
	{
		return *(m_plstFrame->begin());
	}

	std::list<CMVDataFrame*>::iterator i = m_plstFrame->begin();
	for( ; i != m_plstFrame->end(); i++ )
	{
		if( (*i) == curr_frame )
		{
			i++;
			if( i != m_plstFrame->end() )
			{
				return (*i);
			}

			break;
		}

	}

	return NULL;
}

// Add a frame to MVData, if there is already a same frame in MVData, it will be updated.
int CMVData::AddFrame( const CMVDataFrame * frame )
{

	if( !m_bReadyToUse )
	{
		return -1;
	}

	std::list<CMVDataFrame*>::iterator i=m_plstFrame->begin();

	CMVDataFrame * pNewFrame = new CMVDataFrame( *frame );

	// Find the insert point
	while ( i != m_plstFrame->end() && pNewFrame->GetFrameNumber() > (*i)->GetFrameNumber() )
	{
		i++;
	}

	if( i == m_plstFrame->end() )
	{
		// nothing found, reach the end of the list
		m_plstFrame->push_back( pNewFrame );
	}
	else
	{
		
		if( pNewFrame->GetFrameNumber() > (*i)->GetFrameNumber() )
		{
			// should insert the new item before the current item
			m_plstFrame->insert( i, pNewFrame );
		}
		else
		{
			// there is already an item with the same frame number, update it
			delete *i;
			m_plstFrame->remove( *i );
			m_plstFrame->insert( i, pNewFrame );
		}
	}

	//TODO modifiled
	// m_bModified = true;
	return 0;			
}

// Remove a frame by its frame number, if there is no such a frame, nothing will happen.
int CMVData::RemoveFrameByNumber( int framenumber )
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	CMVDataFrame * frame = _GetFrameByNumber( framenumber );
	if ( NULL != frame )
	{
		m_plstFrame->remove( frame );
		m_bModified = true;
		delete frame;
	}

	return 0;
}

const std::string CMVData::GetDataName() const
{
	return m_strDataName;
}



// 结束使用此CMVData中的数据
int CMVData::Close()
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	WriteAll();
	m_pAgent->Close();
	return 0;
}

int CMVData::ReadFrame( int frame_number )
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	if ( frame_number >= 0 )
	{
		m_iLastReadFrameNumber = frame_number;
		return 0;
	}
	else
	{
		return -1;
	}
}

int CMVData::WriteFrame( int frame_number )
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	if ( frame_number >= 0 )
	{
		m_iLastWriteFrameNumber = frame_number;
		return 0;
	}
	else
	{
		return -1;
	}
}


// 取得指定帧的类型
TYPE_FRAME CMVData::GetFrameType( int frame_number ) const
{
	if( !m_bReadyToUse )
	{
		return TYPE_FRAME_MAX;
	}

	CMVDataFrame * pFrame = _GetRealFrameForRead( frame_number );
	if ( NULL != pFrame )
	{
		return pFrame->GetFrameType();
	}
	else
	{
		return TYPE_FRAME_MAX;
	}
}

// 设置指定帧的类型
int CMVData::SetFrameType( TYPE_FRAME frame_type, int frame_number )
{

	if( !m_bReadyToUse )
	{
		return -1;
	}

	CMVDataFrame * pFrame = _GetRealFrameForWrite( frame_number );
	if ( NULL == pFrame )
	{
		return -1;
	}
	else
	{
		return pFrame->SetFrameType( frame_type );	
	}
}

// 根据MB位置以及Frame号取得相应的CMVDataMB，frame_number不指定时，返回上次使用的帧号，失败返回NULL
const CMVDataMB * CMVData::GetMB( int x, int y, int frame_number ) const
{

	if( !m_bReadyToUse )
	{
		return NULL;
	}

	CMVDataFrame * pFrame = _GetRealFrameForRead( frame_number );
	if ( NULL == pFrame )
	{
		return NULL;
	}
	else
	{
		return pFrame->GetMBByCoodinate( x, y );
	}
}

const CMVDataMB * CMVData::GetNextMB( const CMVDataMB * curr_mb, int frame_number ) const
{
	if( !m_bReadyToUse )
	{
		return NULL;
	}

	CMVDataFrame * pFrame = _GetRealFrameForRead( frame_number );
	if ( NULL == pFrame )
	{
		return NULL;
	}
	else
	{
		return pFrame->GetNextMB( curr_mb );
	}
}


int CMVData::AddMB( const CMVDataMB * mb, int frame_number )
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	CMVDataFrame * pFrame = _GetRealFrameForWrite( frame_number );

	if ( NULL == pFrame )
	{
		// 没有对应帧，需要自动添加
		if( frame_number == -1 )
		{
			if( m_iLastWriteFrameNumber > -1 )
			{
				frame_number = m_iLastWriteFrameNumber;
			}
			else
			{
				return NULL;
			}
		}

		// TODO how to get the frame type?
		pFrame = new CMVDataFrame( frame_number, TYPE_FRAME_P_SLICE );
		// TODO return ?
		pFrame->AddMB( mb );
		AddFrame( pFrame );
		return 0;
	}
	else
	{
		return pFrame->AddMB( mb );
	}

}

int CMVData::RemoveMB( int x, int y, int frame_number )
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	CMVDataFrame * pFrame = _GetRealFrameForWrite( frame_number );

	if ( NULL == pFrame )
	{
		return -1;
	}

	return pFrame->RemoveMB( x, y );
}

int CMVData::GetFrameCount() const
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	return static_cast<int>( m_plstFrame->size() );
}

int CMVData::GetMBCount( int frame_number ) const
{
	if( !m_bReadyToUse )
	{
		return -1;
	}

	CMVDataFrame * pFrame = _GetRealFrameForRead( frame_number );

	if ( NULL == pFrame )
	{
		return -1;
	}

	return pFrame->GetMBCount();
}


CMVDataFrame * CMVData::_GetRealFrameForRead( int frame_number ) const
{
	if( !m_bReadyToUse )
	{
		return NULL;
	}

	if( -1 == frame_number )
	{
		if( -1 != m_iLastReadFrameNumber )
		{
			frame_number = m_iLastReadFrameNumber;
		}
		else
		{
			return NULL;
		}
	}

	return _GetFrameByNumber( frame_number );
}

CMVDataFrame * CMVData::_GetRealFrameForWrite( int frame_number ) const
{
	if( !m_bReadyToUse )
	{
		return NULL;
	}

	if( -1 == frame_number )
	{
		if( -1 != m_iLastWriteFrameNumber )
		{
			frame_number = m_iLastWriteFrameNumber;
		}
		else
		{
			return NULL;
		}
	}

	return _GetFrameByNumber( frame_number );
}



bool CMVData::IsDataReady() const
{
	return m_bReadyToUse;
}

int CMVData::GetFrameWidth() const
{
	return m_iFrameWidth;
}

int CMVData::GetFrameHeight() const
{
	return m_iFrameHeight;
}

CMVData& CMVData::operator =( const CMVData& mvdata )
{
	if( !mvdata.IsDataReady() || !IsDataReady() )
	{
		return *this;
	}

	Clear();

	std::list<CMVDataFrame*>::const_iterator iterFrame;
	for( iterFrame = mvdata.m_plstFrame->begin(); iterFrame != mvdata.m_plstFrame->end(); iterFrame++ )
	{
		AddFrame( *iterFrame );
	}

	m_bModified = true;

	return *this;
}


int CMVData::SetFrameSize( int width, int height )
{
	if( width < 0 || height < 0 )
	{
		return -1;
	}

	m_iFrameWidth = width;
	m_iFrameHeight = height;

	m_bModified = true;

	return 0;
	
}
